package com.gitee.easyopen.support;

import java.net.URI;
import java.net.URISyntaxException;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.oltu.oauth2.common.exception.OAuthSystemException;
import org.apache.oltu.oauth2.common.message.OAuthResponse;
import org.apache.velocity.VelocityContext;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.core.io.ClassPathResource;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

import com.gitee.easyopen.ApiConfig;
import com.gitee.easyopen.Invoker;
import com.gitee.easyopen.auth.Oauth2Manager;
import com.gitee.easyopen.auth.Oauth2Service;
import com.gitee.easyopen.doc.ApiDocBuilder;
import com.gitee.easyopen.doc.ApiDocHolder;
import com.gitee.easyopen.exception.LoginErrorException;
import com.gitee.easyopen.register.AbstractInitializer;
import com.gitee.easyopen.util.VelocityUtil;

/**
 * 提供API访问能力,新建一个类继承这个即可.RequestMapping中的value自己定义
 * 
 * <pre>
 * &#64;Controller
 * &#64;RequestMapping(value = "/api")
 * public class IndexController extends ApiController {
 * }
 * 
 * 这样接口的统一访问路径为:http://ip:port/contextPath/api
 * </pre>
 */
public abstract class ApiController extends AbstractInitializer implements ApplicationListener<ContextRefreshedEvent> {

    private ApiConfig apiConfig;
    
    @Autowired(required = false)
    private Oauth2Manager oauth2Manager;
    
    private Oauth2Service oauth2Service;
    
    private Invoker invoker;

    // spring容器加载完毕后执行
    @Override
    public void onApplicationEvent(ContextRefreshedEvent event) {
        this.apiConfig = new ApiConfig();
        this.initApiConfig(this.apiConfig);

        this.init(event.getApplicationContext(), this.apiConfig);
        
        oauth2Service = apiConfig.initOauth2Service(oauth2Manager);
        invoker = this.apiConfig.getInvoker();
    }

    /**
     * 请求入口
     * 
     * @param request
     * @param response
     * @throws Throwable
     */
    @RequestMapping(method = RequestMethod.POST)
    public void index(HttpServletRequest request, HttpServletResponse response) throws Throwable {
        // 调用接口方法,即调用被@Api标记的方法
        this.invoker.invoke(request, response);
    }

    /**
     * 文档页面
     * 
     * @param request
     * @param response
     * @throws Throwable
     */
    @RequestMapping("doc")
    public void doc(HttpServletRequest request, HttpServletResponse response) throws Throwable {
        response.setCharacterEncoding("UTF-8");
        response.setContentType("text/html; charset=UTF-8");
        response.setHeader("Pragma", "No-cache");
        response.setHeader("Cache-Control", "no-cache");
        response.setDateHeader("Expires", 0);

        if (!this.apiConfig.isShowDoc()) {
            response.getWriter().write("文档功能未开启");
            return;
        }

        ClassPathResource res = new ClassPathResource("/doc/index.html");

        VelocityContext context = new VelocityContext();
        ApiDocBuilder docBuilder = ApiDocHolder.getApiDocBuilder();
        context.put("docEntrys", docBuilder.getDocItemEntry());
        String url = request.getRequestURL().toString();
        context.put("url", url.substring(0, url.length() - 4));

        VelocityUtil.generate(context, res.getInputStream(), response.getWriter());
    }

    /**
     * oauth2认证获取code
     * @param model
     * @param request
     * @return
     * @throws URISyntaxException
     * @throws OAuthSystemException
     */
    @RequestMapping("authorize")
    public Object authorize(HttpServletRequest request,HttpServletResponse resp) throws URISyntaxException, OAuthSystemException {
        try {
            OAuthResponse response = oauth2Service.authorize(request, resp, apiConfig);
            HttpHeaders headers = new HttpHeaders();
            headers.setLocation(new URI(response.getLocationUri()));
            return new ResponseEntity<String>(headers, HttpStatus.valueOf(response.getResponseStatus()));
        } catch (LoginErrorException e) {
            return null;
        }
    }

    /**
     * 通过code获取accessToken
     * @param request
     * @return
     * @throws URISyntaxException
     * @throws OAuthSystemException
     */
    @RequestMapping(value = "accessToken", method=RequestMethod.POST)
    public HttpEntity<?> accessToken(HttpServletRequest request) throws URISyntaxException, OAuthSystemException {
        OAuthResponse response = oauth2Service.accessToken(request, apiConfig);
        return new ResponseEntity<String>(  
                response.getBody(), HttpStatus.valueOf(response.getResponseStatus())); 
    }
    
    // 捕捉异常
    @ExceptionHandler(value = Throwable.class)
    public void jsonErrorHandler(HttpServletRequest req, HttpServletResponse response, Throwable e) throws Exception {
        this.invoker.responseResult(response, e);
    }

    protected abstract void initApiConfig(ApiConfig apiConfig);

}
